package com.jingdun.sport.sporttype

import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.jingdun.sport.authority.Roles
import com.jingdun.sport.authority.SecurityChecker
import com.jingdun.sport.bean.model.ApiResult
import com.jingdun.sport.bean.model.User
import com.jingdun.sport.common.util.PageParam
import com.jingdun.sport.data.environment.DataEnvironment
import com.jingdun.sport.data.environment.intGeneratedKey
import com.jingdun.sport.data.environment.page
import com.jingdun.sport.data.environment.select
import com.losttemple.sql.language.dialects.DialectEnvironment
import com.losttemple.sql.language.operator.*
import com.losttemple.sql.language.reflection.allFields
import com.losttemple.sql.language.reflection.insertObject
import com.losttemple.sql.language.reflection.useAll
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import java.net.URI
import kotlin.io.use

class SportType {
    var parent: Int? = null
    var name: String? = null
    var description: String? = null
    var supportTiming: Boolean = true
    var supportCounting: Boolean = true
    var trainVideo: String? = null
    var introduceVideo: String? = null
    var mainLogo: String? = null
}

class SportTypeP {
    var id: Int = 0
    var name: String? = null
    var description: String? = null
    var supportTiming: Boolean = true
    var supportCounting: Boolean = true
    var trainVideo: String? = null
    var introduceVideo: String? = null
    var mainLogo: String? = null
}

@RestController
@RequestMapping("/sport/types/")
class SportTypeController {
    @Autowired
    private lateinit var sql: DataEnvironment

    @Autowired
    private lateinit var securityChecker: SecurityChecker

    @PostMapping("/addType")
    fun addType(@RequestBody sportType: SportType): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        val typeId = sql.run {
            val configId = db { SportConfig(it) }.insert {
                it.name(sportType.name)
                it.description(sportType.description)
                it.supportTiming(sportType.supportTiming)
                it.supportCounting(sportType.supportCounting)
                it.trainVideo(sportType.trainVideo)
                it.introduceVideo(sportType.introduceVideo)
                it.mainLogo(sportType.mainLogo)
            }.ret {
                intGeneratedKey()
            }()!!

            val type = db { SportTypes(it) }
                    .insert {
                        it.currentConfig(configId)
                        it.enabled(true)
                        if (sportType.parent == null) {
                            it.parentId(0)
                        } else {
                            it.parentId(sportType.parent)
                        }
                    }
                    .ret {
                        intGeneratedKey()
                    }()!!
            db { SportConfig(it) }
                    .where { id eq configId }
                    .update { it.type(type) }()
            val parent = sportType.parent
            if (parent != null) {
                val ancestors = from { SportTypeRelations(it) }
                        .where { child eq parent }
                        .use { need(it.ancestor) }
                        .select {
                            it.ancestor()
                        }

                for (ancestor in ancestors) {
                    db { SportTypeRelations(it) }
                            .insert {
                                it.ancestor(ancestor)
                                it.child(type)
                            }()
                }
                db { SportTypeRelations(it) }
                        .insert {
                            it.ancestor(parent)
                            it.child(type)
                        }()
            }
            type
        }
        //return ResponseEntity.created(URI.create("/sport/types/${typeId}")).build()
        apiResult.data = "/sport/types/${typeId}"
        return ResponseEntity.ok(apiResult)
    }

    @PutMapping
    fun updateType(@RequestBody sportType: SportTypeP): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        sql.run {
            val currentConfig = (from { SportTypes(it) }
                    .where { id eq sportType.id }
                    .use { need(it.currentConfig) }
                    .select { it.currentConfig() })
                    .first() ?: return ResponseEntity.ok(apiResult)
            val updatedConfigId = db { SportConfig(it) }
                    .insert {
                        it.name(sportType.name)
                        it.description(sportType.description)
                        it.supportTiming(sportType.supportTiming)
                        it.supportCounting(sportType.supportCounting)
                        it.trainVideo(sportType.trainVideo)
                        it.introduceVideo(sportType.introduceVideo)
                        it.mainLogo(sportType.mainLogo)
                        it.lastConfig(currentConfig)
                        it.type(sportType.id)
                    }
                    .ret {
                        intGeneratedKey()
                    }()!!
            db { SportTypes(it) }
                    .where { id eq sportType.id }
                    .update { it.currentConfig(updatedConfigId) }()
            apiResult.data = "updated"
            return ResponseEntity.ok(apiResult)
        }
    }

    @GetMapping("/{type}")
    fun typeDetail(@PathVariable("type") typeId: Int): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        val type = sql.run {
            (from { SportTypes(it) } innerJoin from { SportConfig(it) })
                    .select {
                        object {
                            val type = left
                            val config = right
                        }
                    }
                    .on { type.currentConfig eq config.id }
                    .where { type.id eq typeId }
                    .use {
                        need(it.type.id)
                        need(it.type.parentId)
                        need(it.config.description)
                        need(it.config.supportTiming)
                        need(it.config.supportCounting)
                        need(it.config.trainVideo)
                        need(it.config.introduceVideo)
                        need(it.config.mainLogo)
                    }
                    .select {
                        object {
                            val id = it.type.id()
                            val parent = it.type.parentId()
                            val description = it.config.description()
                            val supportTiming = it.config.supportTiming()
                            val supportCounting = it.config.supportCounting()
                            val trainVideo = it.config.trainVideo()
                            val introduceVideo = it.config.introduceVideo()
                            val mainLogo = it.config.mainLogo()
                        }
                    }.firstOrNull()
        } ?: return ResponseEntity.ok(apiResult)
        apiResult.data = type
        return ResponseEntity.ok(apiResult)
    }

    @GetMapping("/configs/{config}")
    fun configDetail(@PathVariable("config") configId: Int): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        val type = sql.run {
            from { SportConfig(it) }
                    .where { id eq configId }
                    .use {
                        need(it.description)
                        need(it.supportTiming)
                        need(it.supportCounting)
                        need(it.trainVideo)
                        need(it.introduceVideo)
                        need(it.mainLogo)
                        need(it.type)
                    }
                    .select {
                        object {
                            val description = it.description()
                            val supportTiming = it.supportTiming()
                            val supportCounting = it.supportCounting()
                            val trainVideo = it.trainVideo()
                            val introduceVideo = it.introduceVideo()
                            val mainLogo = it.mainLogo()
                            val type = it.type()
                        }
                    }.firstOrNull()
        } ?: return ResponseEntity.ok(apiResult)
        apiResult.data = type
        return ResponseEntity.ok(apiResult)
    }

    @GetMapping("/listTypes")
    fun listTypes(parent: Int, page: PageParam<*>): ResponseEntity<Any> {
        val apiResult: ApiResult<*> = ApiResult<Any?>()
        val result = sql.run {
            ((sportTypeDetail() innerJoin from { SportTypeRelations(it) })
                    .on { type, relation -> type.id eq relation.child }
                    .select { type, relation ->
                        object {
                            val id = type.id
                            val ancestor = relation.ancestor
                            val enabled = type.enabled
                            val config = type.config
                            var mainLogo = type.config.mainLogo
                            var name = type.config.name
                            var parent = type.parentId
                        }
                    })
                    .where { enabled eq true }
                    .where { ancestor eq parent }
                    .order { id }
                    .page(page)
                    .with(allFields())
                    .select {
                        object {
                            val id = it.id()
                            var name = it.name()
                            val mainLogo = it.mainLogo()
                            val parent = it.parent()
                        }
                    }
        }
        apiResult.data = result
        return ResponseEntity.ok(apiResult)
    }
}